/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the EternalTech source code.

 The EternalTech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The EternalTech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the EternalTech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// sg_spawn.cpp - spawning entities
//


#include "sg_local.h"


extern void				SG_Light2_Spawn (etEntity *self);
extern void				SG_LightFlameSmallYellow_Spawn (etEntity *self);
extern void				SG_LightFlameLargeYellow_Spawn (etEntity *self);
extern void				SG_LightFlameSmallWhite_Spawn (etEntity *self);
extern void				SG_LightTorchSmallWallTorch_Spawn (etEntity *self);

extern void				SG_Speaker_Spawn (etEntity *self);
extern void				SG_AmbientSuckWind_Spawn (etEntity *self);
extern void				SG_AmbientDrone_Spawn (etEntity *self);
extern void				SG_AmbientFlouroBuzz_Spawn (etEntity *self);
extern void				SG_AmbientDrip_Spawn (etEntity *self);
extern void				SG_AmbientCompHum_Spawn (etEntity *self);
extern void				SG_AmbientThunder_Spawn (etEntity *self);
extern void				SG_AmbientLightBuzz_Spawn (etEntity *self);
extern void				SG_AmbientSwamp1_Spawn (etEntity *self);
extern void				SG_AmbientSwamp2_Spawn (etEntity *self);

extern void				SG_InfoIntermission_Spawn (etEntity *self);
extern void				SG_InfoNotNULL_Spawn (etEntity *self);
extern void				SG_InfoNULL_Spawn (etEntity *self);
extern void				SG_InfoPlayerCoop_Spawn (etEntity *self);
extern void				SG_InfoPlayerDeathmatch_Spawn (etEntity *self);
extern void				SG_InfoPlayerStart_Spawn (etEntity *self);
extern void				SG_InfoPlayerStart2_Spawn (etEntity *self);
extern void				SG_InfoTeleportDestination_Spawn (etEntity *self);

extern void				SG_FuncDoor_Spawn (etEntity *self);
extern void				SG_FuncDoorSecret_Spawn (etEntity *self);

extern void				SG_TriggerCounter_Spawn (etEntity *self);
extern void				SG_TriggerOnce_Spawn (etEntity *self);
extern void				SG_TriggerMultiple_Spawn (etEntity *self);
extern void				SG_TriggerTeleport_Spawn (etEntity *self);

extern void				SG_ItemHealth_Spawn (etEntity *self);
extern void				SG_ItemArmor1_Spawn (etEntity *self);
extern void				SG_ItemArmor2_Spawn (etEntity *self);
extern void				SG_ItemArmorInv_Spawn (etEntity *self);

extern void				SG_ItemShells_Spawn (etEntity *self);
extern void				SG_ItemSpikes_Spawn (etEntity *self);
extern void				SG_ItemRockets_Spawn (etEntity *self);
extern void				SG_ItemCells_Spawn (etEntity *self);

extern void				SG_ItemKey1_Spawn (etEntity *self);
extern void				SG_ItemKey2_Spawn (etEntity *self);

extern void				SG_ItemSigil_Spawn (etEntity *self);

extern void				SG_ItemArtifactInvulnerability_Spawn (etEntity *self);
extern void				SG_ItemArtifactEnvirosuit_Spawn (etEntity *self);
extern void				SG_ItemArtifactInvisibility_Spawn (etEntity *self);
extern void				SG_ItemArtifactSuperDamage_Spawn (etEntity *self);

extern void				SG_WeaponSuperShotgun_Spawn (etEntity *self);
extern void				SG_WeaponNailgun_Spawn (etEntity *self);
extern void				SG_WeaponSuperNailgun_Spawn (etEntity *self);
extern void				SG_WeaponGrenadeLauncher_Spawn (etEntity *self);
extern void				SG_WeaponRocketLauncher_Spawn (etEntity *self);
extern void				SG_WeaponLightning_Spawn (etEntity *self);

extern void				SG_MonsterArmy_Spawn (etEntity *self);
extern void				SG_MonsterEnforcer_Spawn (etEntity *self);
extern void				SG_MonsterDog_Spawn (etEntity *self);
extern void				SG_MonsterOgre_Spawn (etEntity *self);
extern void				SG_MonsterKnight_Spawn (etEntity *self);
extern void				SG_MonsterZombie_Spawn (etEntity *self);
extern void				SG_MonsterWizard_Spawn (etEntity *self);
extern void				SG_MonsterDemon1_Spawn (etEntity *self);
extern void				SG_MonsterShambler_Spawn (etEntity *self);
extern void				SG_MonsterShalrath_Spawn (etEntity *self);
extern void				SG_MonsterBoss_Spawn (etEntity *self);

struct spawnFunction_t {
	const char *			name;
	void					(*spawn)(etEntity *self);
};

static spawnFunction_t		sg_spawnFunctions[] = {
	{"light2",							SG_Light2_Spawn},
	{"light_flame_small_yellow",		SG_LightFlameSmallYellow_Spawn},
	{"light_flame_large_yellow",		SG_LightFlameLargeYellow_Spawn},
	{"light_flame_small_white",			SG_LightFlameSmallWhite_Spawn},
	{"light_torch_small_walltorch",		SG_LightTorchSmallWallTorch_Spawn},

	{"speaker",							SG_Speaker_Spawn},
	{"ambient_suck_wind",				SG_AmbientSuckWind_Spawn},
	{"ambient_drone",					SG_AmbientDrone_Spawn},
	{"ambient_flouro_buzz",				SG_AmbientFlouroBuzz_Spawn},
	{"ambient_drip",					SG_AmbientDrip_Spawn},
	{"ambient_comp_hum",				SG_AmbientCompHum_Spawn},
	{"ambient_thunder",					SG_AmbientThunder_Spawn},
	{"ambient_light_buzz",				SG_AmbientLightBuzz_Spawn},
	{"ambient_swamp1",					SG_AmbientSwamp1_Spawn},
	{"ambient_swamp2",					SG_AmbientSwamp2_Spawn},

	{"info_teleport_destination",		SG_InfoTeleportDestination_Spawn},
	{"info_intermission",				SG_InfoIntermission_Spawn},
	{"info_notnull",					SG_InfoNotNULL_Spawn},
	{"info_null",						SG_InfoNULL_Spawn},
	{"info_player_coop",				SG_InfoNULL_Spawn},
	{"info_player_deathmatch",			SG_InfoPlayerCoop_Spawn},
	{"info_player_start",				SG_InfoPlayerStart_Spawn},
	{"info_player_start2",				SG_InfoPlayerStart2_Spawn},
	{"info_teleport_destination",		SG_InfoTeleportDestination_Spawn},

	{"func_door",						SG_FuncDoor_Spawn},
	{"func_door_secret",				SG_FuncDoorSecret_Spawn},

	{"trigger_counter",					SG_TriggerCounter_Spawn},
	{"trigger_once",					SG_TriggerOnce_Spawn},
	{"trigger_multiple",				SG_TriggerMultiple_Spawn},
	{"trigger_teleport",				SG_TriggerTeleport_Spawn},

	{"item_health",						SG_ItemHealth_Spawn},
	{"item_armor1",						SG_ItemArmor1_Spawn},
	{"item_armor2",						SG_ItemArmor2_Spawn},
	{"item_armorinv",					SG_ItemArmorInv_Spawn},

	{"item_shells",						SG_ItemShells_Spawn},
	{"item_spikes",						SG_ItemSpikes_Spawn},
	{"item_rockets",					SG_ItemRockets_Spawn},
	{"item_cells",						SG_ItemCells_Spawn},

	{"item_key1",						SG_ItemKey1_Spawn},
	{"item_key2",						SG_ItemKey2_Spawn},

	{"item_sigil",						SG_ItemSigil_Spawn},

	{"item_artifact_invulnerability",	SG_ItemArtifactInvulnerability_Spawn},
	{"item_artifact_envirosuit",		SG_ItemArtifactEnvirosuit_Spawn},
	{"item_artifact_invisibility",		SG_ItemArtifactInvisibility_Spawn},
	{"item_artifact_super_damage",		SG_ItemArtifactSuperDamage_Spawn},

	{"weapon_supershotgun",				SG_WeaponSuperShotgun_Spawn},
	{"weapon_nailgun",					SG_WeaponNailgun_Spawn},
	{"weapon_supernailgun",				SG_WeaponSuperNailgun_Spawn},
	{"weapon_grenadelauncher",			SG_WeaponGrenadeLauncher_Spawn},
	{"weapon_rocketlauncher",			SG_WeaponRocketLauncher_Spawn},
	{"weapon_lightning",				SG_WeaponLightning_Spawn},

	{"monster_army",					SG_MonsterArmy_Spawn},
	{"monster_enforcer",				SG_MonsterEnforcer_Spawn},
	{"monster_dog",						SG_MonsterDog_Spawn},
	{"monster_ogre",					SG_MonsterOgre_Spawn},
	{"monster_knight",					SG_MonsterKnight_Spawn},
	{"monster_zombie",					SG_MonsterZombie_Spawn},
	{"monster_wizard",					SG_MonsterWizard_Spawn},
	{"monster_demon1",					SG_MonsterDemon1_Spawn},
	{"monster_shambler",				SG_MonsterShambler_Spawn},
	{"monster_shalrath",				SG_MonsterShalrath_Spawn},
	{"monster_boss",					SG_MonsterBoss_Spawn},

	{NULL,								NULL}
};


/*
 ==================
 SG_WorldSpawn
 ==================
*/
static void SG_WorldSpawn (const etDict &dict){

	int		musicTrack;

	// Make sure the class name is "worldSpawn"
	if (Str_ICompare(dict.GetString("className"), "worldSpawn"))
		G_Error("SG_WorldSpawn: the first entity must be 'worldSpawn'");

	// Set config strings
	SG_SetConfigString(CS_VERSION, GAME_VERSION);
	SG_SetConfigString(CS_LEVEL_NAME, level.name);

	// Set up the entity
	level.worldEntity = &level.entities[ENTITYNUM_WORLD];

	level.worldEntity->Setup(ENTITYNUM_WORLD, ET_BAD, "worldSpawn", "worldSpawn", &dict);

	// Get the gravity
	if (!Str_ICompare(level.name, "e1m8"))
		glqCVarSystem->SetFloat(sg_gravity, 100.0f);
	else
		glqCVarSystem->SetFloat(sg_gravity, DEFAULT_GRAVITY);

	SG_SetConfigString(CS_GRAVITY, Str_FromFloat(sg_gravity->floatValue, -2));

	// Get the music track
	musicTrack = dict.GetInteger("sounds");

	SG_SetConfigString(CS_MUSIC, Str_FromInteger(musicTrack));
}

/*
 ==================
 SG_CallSpawn

 Finds the spawn function for the entity and calls it, returning false if not
 found.
 ==================
*/
static bool SG_CallSpawn (etEntity *entity){

	spawnFunction_t	*sf;

	// Check spawn functions
	for (sf = sg_spawnFunctions; sf->name; sf++){
		if (!Str_ICompare(sf->name, entity->className)){
			sf->spawn(entity);
			return true;
		}
	}

	G_Printf(S_COLOR_RED "Entity class '%s' doesn't have a spawn function\n", entity->className);

	return false;
}

/*
 ==================
 SG_SpawnEntity
 ==================
*/
etEntity *SG_SpawnEntity (const etDict &dict){

	etEntity	*entity;
	const char	*name, *className;

	// Get the name and class name
	dict.GetString("name", &name);
	dict.GetString("className", &className);

	if (!className || !className[0])
		G_Error("SG_SpawnEntity: NULL class name");

	// Allocate an entity
	entity = SG_AllocEntity(name, className, &dict);

	// Call the spawn function
	if (!SG_CallSpawn(entity)){
		// If we didn't find a spawn function, don't bother with it
		SG_FreeEntity(entity);

		return NULL;
	}

	// If we already spawned all the entities in the level
	if (level.state == GS_ACTIVE){

	}

	return entity;
}

/*
 ==================
 SG_SpawnEntities

 Parses textual entity definitions out of a BSP entity string and spawns entities
 ==================
*/
void SG_SpawnEntities (const char *entityString){

	script_t	*script;
	etDict		dict;
	int			spawned;

	G_Printf("Spawning entities\n");

	// Load the script file
	script = glqParser->LoadScriptMemory("EntityString", entityString, Str_Length(entityString), 1);
	if (!script)
		G_Error("SG_SpawnEntities: no entities");

	glqParser->SetScriptFlags(script, SF_NOFATALERRORS | SF_ALLOWESCAPECHARS);

	// The "worldSpawn" is not an actual entity, but it still has a spawn
	// function to perform any global setup needed by a level
	if (!dict.Parse(script))
		G_Error("SG_SpawnEntities: no entities");

	SG_WorldSpawn(dict);

	// Spawn the entities
	spawned = 1;

	while (1){
		dict.Clear();

		if (!dict.Parse(script))
			break;

		// Spawn an entity
		if (!SG_SpawnEntity(dict))
			continue;

		spawned++;
	}

	glqParser->FreeScript(script);

	G_Printf("...%i entities spawned\n", spawned);
}

/*
 ==================
 SG_SpawnLightAndSpeakerEntities

 FIXME: why does the first entity never get parsed?, does it execpt the first entity to be "worldSpawn"?
 ==================
*/
void SG_SpawnLightAndSpeakerEntities (){

	char		name[MAX_PATH_LENGTH];
	script_t	*script;
	etDict		dict;
	int			spawned;

	G_Printf("Spawning light and speaker entities\n");

	// Load the script file
	Str_SPrintf(name, sizeof(name), "maps/%s.ent2", level.name);

	script = glqParser->LoadScriptFile(name);
	if (!script){
		G_Printf(S_COLOR_RED "SG_SpawnLightAndSpeakerEntities: no entities");
		return;
	}

	glqParser->SetScriptFlags(script, SF_NOFATALERRORS | SF_ALLOWESCAPECHARS);

	// Parse the entities
	if (!dict.Parse(script))
		G_Error("SG_SpawnLightAndSpeakerEntities: no entities");

	// Spawn the entities
	spawned = 0;

	while (1){
		dict.Clear();

		if (!dict.Parse(script))
			break;

		// Spawn an entity
		if (!SG_SpawnEntity(dict))
			continue;

		spawned++;
	}

	glqParser->FreeScript(script);

	G_Printf("...%i entities spawned\n", spawned);
}